package com.mdd.common.pay.wxpay;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import com.mdd.common.util.QrCode;
import com.mdd.common.util.RedisUtils;
import com.mdd.common.util.StringUtils;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.exception.ParseException;
import com.wechat.pay.contrib.apache.httpclient.exception.ValidationException;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 	微信支付处理
 * @author DEREK
 *
 */
@Component
@Slf4j
public class WxpayHandle {

	@Autowired
	private WxConfig wxConfig;
	
	/**
	 * 元转换成分
	 * 微信交易单位为分
	 */
	private String castToCent(String amount) {
		if (amount == null) {
			return "";
		}
		return String.valueOf(BigDecimal.valueOf(Double.valueOf(amount) * 100).intValue());
	}
	
	/**
	 * 	微信APP支付
	 * @param orderNo		订单号
	 * @param amount		金额
	 * @param remark		备注
	 * @param notifyUrl		回调地址
	 * @return
	 */
	public Map<String,Object> createWxPayOrder(String orderNo, String amount, String remark, String notifyUrl) {
		Map<String,Object> retMap = new HashMap<String, Object>();
		try {
			WXPay wxpay = new WXPay(wxConfig);
			Map<String, String> data = new HashMap<>();
			//微信开放平台审核通过的应用APPID（请登录open.weixin.qq.com查看，注意与公众号的APPID不同）
			data.put("appid", wxConfig.getAppID());
			//微信支付分配的商户号
			data.put("mch_id", wxConfig.getMchID());
			//            data.put("mch_id", wxConfig.getMchID());
			//data.put("openid", openId);
			//随机字符串，不长于32位
			data.put("nonce_str", WXPayUtil.generateNonceStr());
			//APP——需传入应用市场上的APP名字-实际商品名称，天天爱消除-游戏充值。
			data.put("body", "快点嘛");
			//商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一
			data.put("out_trade_no", orderNo);
			//订单总金额，单位为分
			data.put("total_fee", castToCent(amount));
			//自己的服务器IP地址
			data.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress());
			//异步通知地址（请注意必须是外网）
			data.put("notify_url", notifyUrl);
			//交易类型
			data.put("trade_type", WxPayConfigUtils.TRADETYPEAPP);
			//附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
			data.put("attach", "");
			data.put("sign", WXPayUtil.generateSignature(data, wxConfig.getKey(),
					WXPayConstants.SignType.MD5));
			//使用官方API请求预付订单
			Map<String, String> response = wxpay.unifiedOrder(data);
			if ("SUCCESS".equals(response.get("return_code"))) {//主要返回以下5个参数
				Map<String, String> param = new HashMap<>();
				param.put("appid", wxConfig.getAppID());
				param.put("partnerid", response.get("mch_id"));
				param.put("prepayid", response.get("prepay_id"));
				param.put("package", "Sign=WXPay");
				param.put("noncestr", WXPayUtil.generateNonceStr());
				param.put("timestamp", System.currentTimeMillis() / 1000 + "");
				param.put("sign", WXPayUtil.generateSignature(param, wxConfig.getKey(),
						WXPayConstants.SignType.MD5));
				retMap.put("body", JSON.toJSONString(param));
			} else {
				retMap.put("body", response.get("return_msg"));
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return retMap;
	}
	
	/**
	 * 	微信原路退回
	 * @param tradeNo			微信订单号
	 * @param amount			退款金额
	 * @param orderRefundNo		退款订单号
	 * @param notifyUrl			回调地址
	 * @return
	 */
	public String wxPayOriginalRefund(String tradeNo,BigDecimal amount,String orderRefundNo,String notifyUrl){
		try {
			BigDecimal totalPrice = amount.multiply(new BigDecimal(100));
			Map<String, Object> data = new HashMap<String, Object>();
			data.put("out_trade_no", tradeNo);
			data.put("out_refund_no", orderRefundNo);
			data.put("notify_url", notifyUrl);
			Map<String, Object> amountMap = new HashMap<String, Object>();
			//退款金额
			amountMap.put("refund", totalPrice.intValue());
			amountMap.put("total", totalPrice.intValue());
			amountMap.put("currency", "CNY");
			data.put("amount", amountMap);
			System.out.println("退款参数："+JSONObject.toJSONString(data));
			String refundUrl = "/v3/refund/domestic/refunds";
			String privateKeyPath = WxPayConfigUtils.CERPEMPROD;
			//请求退款
	        IJPayHttpResponse response = WxPayApi.v3(
	                RequestMethod.POST,
	                WxDomain.CHINA.toString(),
	                refundUrl,
	                // 商户号
	                WxPayConfigUtils.MCHID,
	                // 获取证书序列号
	                WxPayConfigUtils.PAYSERIALNO,
	                null,
	                // 私钥
	                privateKeyPath,
	                // 请求参数
	                JSONUtil.toJsonStr(data)
	        );
	        String body = response.getBody();
	        System.out.println("申请微信退款返回参数："+body);
	        //余额不足特殊处理
	        if (StringUtils.isNotBlank(body) && body.contains("NOT_ENOUGH") && body.contains("基本账户余额不足")) {
	        	return body;
	        }
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
					
		}
		return "SUCCESS";
	}
	
	/**
	 * 	微信退款回调处理
	 * @param request
	 * @return
	 * @throws IOException
	 */
	public NotificationRequest getNotificationRequest(HttpServletRequest request) throws IOException {
        //从请求头获取验签字段
        String Timestamp = request.getHeader("Wechatpay-Timestamp");
        String Nonce = request.getHeader("Wechatpay-Nonce");
        String Signature = request.getHeader("Wechatpay-Signature");
        String Serial = request.getHeader("Wechatpay-Serial");
        //读取请求体的信息
        InputStream inputStream = request.getInputStream();
        StringBuffer sb = new StringBuffer();
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        while((s = br.readLine()) != null) {
            sb.append(s);
        }
        String requestBody = String.valueOf(sb);
        return new NotificationRequest.Builder().withSerialNumber(Serial)
                .withNonce(Nonce)
                .withTimestamp(Timestamp)
                .withSignature(Signature)
                .withBody(requestBody)
                .build();
    }
	
	/**
	 * 	微信回调退款处理
	 * @param notificationRequest
	 * @return
	 * @throws NotFoundException
	 * @throws ValidationException
	 * @throws ParseException
	 */
	public Notification getParseResult(NotificationRequest notificationRequest) throws NotFoundException, ValidationException, ParseException {
		CertificatesManager certificatesManager = CertificatesManager.getInstance();
        //验签和解析请求体
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(WxPayConfigUtils.MCHID);
        NotificationHandler handler = new NotificationHandler(verifier, WxPayConfigUtils.APIV3KEY.getBytes(StandardCharsets.UTF_8));
        //在解析微信支付通知请求结果时会自动验签，不通过会抛出异常
        return handler.parse(notificationRequest);
    }
	
	// 扫码支付
	/**
	 * 	微信下单
	 * @param orderNo
	 * @param amount
	 * @param remark
	 * @return
	 */
	public String getQrCode(String orderNo,String amount,String remark,String notifyUrl) {
		String payPath = "";
		try {
			String timeExpire =  new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date());
			// com.ijpay.wxpay.model.v3.UnifiedOrderModel
			UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
					.setAppid(WxPayConfigUtils.QRCODE_APPID)
					.setMchid(WxPayConfigUtils.MCHID)
					.setDescription(remark)
					.setOut_trade_no(orderNo)
					.setTime_expire(timeExpire)
					.setNotify_url(notifyUrl)
					.setAmount(new Amount().setTotal(Integer.valueOf(castToCent(amount))));

			 IJPayHttpResponse response = WxPayApi.v3(
	                    RequestMethod.POST,
	                    WxDomain.CHINA.toString(),
	                    WxApiType.NATIVE_PAY.toString(),
	                    WxPayConfigUtils.MCHID,
	                    getSerialNumber(),
	                    null,
	                    WxPayConfigUtils.WXPAY_KEY_PEM,
	                    JSONUtil.toJsonStr(unifiedOrderModel)
	            );
			// 根据证书序列号查询对应的证书来验证签名结果
			boolean verifySignature = WxPayKit.verifySignature(response, WxPayConfigUtils.WXPAY_PLATFORMCERTPATH);
			log.info("verifySignature:{}",verifySignature);
			log.info("response:{}",response.getBody());
			if(verifySignature) {
				JSONObject json = JSONObject.parseObject(response.getBody());
				payPath = QrCode.codeCreate(json.getString("code_url"));
				RedisUtils.set(orderNo, "getOrder",300);
			}
		} catch (Exception e) {
			e.printStackTrace();
			payPath = "";
		}
		return payPath;
	}
	
	private String getSerialNumber() {
		// 获取证书序列号
		X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(WxPayConfigUtils.WXPAY_CERT_PEM));
		String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
		System.out.println("serialNo:" + serialNo);
		return serialNo;
	}
	
}
